package com.halachawiki.server.data;

import java.util.Date;

import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.EntityNotFoundException;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.Transaction;

public abstract class VersionedEntity {

	public abstract String getTable();
	protected DatastoreService datastore;
	protected Entity entity;
	protected Transaction txn; 
	boolean isNewVersion = false;
	User currentUser;
	
	protected void beginTransaction(boolean expectNew) { 
		if (expectNew && txn != null) {
			throw new RuntimeException("Incorrect transaction management");
		}
		if (txn == null) {
			txn = datastore.beginTransaction();
		}
	}
	public void commit() {
		this.setEditorNC(currentUser.getId());
		this.setModifiedNC(new Date());
		datastore.put(entity);
		txn.commit();
		txn = null;
		if (isNewVersion) {
			LatestVersionMap.setLatestVersionId(this.getEntityId(), this.getTable(), this.getId());
			isNewVersion = false;
		}
	}
	protected void rollback() {
		txn.rollback();
		txn = null;
	}
	
	// Constructor to be used in new entity
	protected VersionedEntity(User currentUser) { 
		isNewVersion = true;
		this.currentUser = currentUser;
		Key entityId = LatestVersionMap.newEntityId(getTable());
		datastore = DatastoreServiceFactory.getDatastoreService();
		beginTransaction(true);
		entity = new Entity(getTable());
		this.setEntityIdNC(entityId);
		this.setTableNC();
	}
	
	// Constructor to be used in new version of existing entity
	protected VersionedEntity(User currentUser, VersionedEntity previous) {
		isNewVersion = true;
		this.currentUser = currentUser;
		datastore = DatastoreServiceFactory.getDatastoreService();
		beginTransaction(true);
		entity = new Entity(getTable());
		this.setEntityIdNC(previous.getEntityId());
		this.setPreviousVersionNC(previous.getId());
		this.setTableNC();
	}

	// Constructor used when loading existing version.
	protected VersionedEntity(User currentUser, Key id, boolean modify) throws EntityNotFoundException {
		datastore = DatastoreServiceFactory.getDatastoreService();
		if (modify)
			txn = datastore.beginTransaction();
		entity = datastore.get(id);
		this.currentUser = currentUser;
	}

	// Begin "special" properties. Setters are "private".
	// Note: will be NULL in new entity.
	public Key getId() {
		return entity.getKey();
	}

	public Key getEntityId() {
		return (Key) entity.getProperty("EntityId");
	}
	private void setEntityIdNC(Key entityId) {
		beginTransaction(false);
		entity.setProperty("EntityId", entityId);
	}

	private void setTableNC() {
		beginTransaction(false);
		entity.setProperty("Table", getTable());
	}
	
	public Key getPreviousVersion() {
		return (Key) entity.getProperty("PreviousVersion");
	}
	private void setPreviousVersionNC(Key previousVersion) {
		beginTransaction(false);
		entity.setProperty("PreviousVersion", previousVersion);
	}
	// end "special" properties"
	

	public Key getNextVersion() {
		return (Key) entity.getProperty("NextVersion");
	}
	public void setNextVersionNC(Key nextVersion) {
		beginTransaction(false);
		entity.setProperty("NextVersion", nextVersion);
	}
	public void setNextVersion(Key nextVersion) {
		beginTransaction(true);
		entity.setProperty("NextVersion", nextVersion);
		commit();
	}

	public Key getEditor() {
		return (Key) entity.getProperty("Editor");
	}
	public void setEditorNC(Key editor) {
		beginTransaction(false);
		entity.setProperty("Editor", editor);
	}
	public void setEditor(Key editor) {
		beginTransaction(true);
		entity.setProperty("Editor", editor);
		commit();
	}

	//public Key approver;
	
	public Date getModified() {
		return (Date) entity.getProperty("Modified");
	}
	public void setModifiedNC(Date modified) {
		beginTransaction(false);
		entity.setProperty("Modified", modified);
	}
	public void setModified(Date modified) {
		beginTransaction(true);
		entity.setProperty("Modified", modified);
		commit();
	}

	//public String changeComment;
	
}
